﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using TracedReality.RayTracing;
using TracedReality.RayTracing.PhotonMapping;
using QiHe.Yaml.Grammar;

namespace TracedReality.Primitive
{
    /// <summary>
    /// A Light that exists as a point.
    /// </summary>
    public class PointLight : Light
    {
        /// <summary>
        /// The position of this PointLight.
        /// </summary>
        public Vector position;

        /// <summary>
        /// Create a PointLight at the given position with the given color.
        /// </summary>
        /// <param name="p">Position of this PointLight.</param>
        /// <param name="c">Color of this PointLight.</param>
        public PointLight(Vector p, Vector c)
        {
            position = p;
            color = c;
        }

        /// <summary>
        /// Determine how much light from this PointLight is received at the given Hit.
        /// Does not use sampling since PointLights do not cause umbras.
        /// </summary>
        /// <param name="ray">The Ray that is being traced.</param>
        /// <param name="hit">The Hit containing the intersection info.</param>
        /// <param name="r">Random for sampling.</param>
        /// <returns>The contribution of this PointLight.</returns>
        public override Vector getContribution(Ray ray, Hit hit, Random r)
        {
            Vector contrib = new Vector();
            Vector point = ray.pointAtParameter(hit.t);
            Vector lightDir = position - point;
            lightDir.normalize();

            if (Settings.numShadowSamples == 0)
            {
                return hit.material.shade(ray, hit, lightDir, color);
            }

            Vector lpDir = position - point;
            double dst = lpDir.length;
            lpDir.normalize();

            Ray shadow = new Ray(point, lpDir);
            if (RayTracer.intersectsAnythingBefore(shadow, dst)) { return new Vector(); }

            return hit.material.shade(ray, hit, lightDir, color);
        }

        /// <summary>
        /// Generates a Photon of the correct power and direction for photon mapping.
        /// </summary>
        /// <param name="num">The number of Photons that will be shot from this PointLight.</param>
        /// <param name="r">Random for sampling.</param>
        /// <returns>A Photon with correct power and direction.</returns>
        public override Photon generatePhoton(int num, Random r)
        {
            Vector dir = Vector.RandomUnitVector(r);

            return new Photon(position, color, dir);
        }

        public static PointLight Parse(MappingEntry entry)
        {
            if (entry == null) { throw new ArgumentNullException("entry"); }

            Mapping map = (Mapping)entry.Value;
            MappingEntry found = map.Find("color");
            if (found == null) { throw new ArgumentException("No color entry"); }
            Vector color = Vector.FromSequence((Sequence)found.Value);
            found = map.Find("position");
            if (found == null) { throw new ArgumentException("No position entry"); }
            Vector position = Vector.FromSequence((Sequence)found.Value);

            return new PointLight(position, color);
        }

        /// <summary>
        /// Gets a string representation of this PointLight.
        /// </summary>
        /// <returns>A string representing this PointLight.</returns>
        public override string ToString()
        {
            return "PointLight: { center: " + position + ", color: " + color + " }";
        }
    }
}
